#include "../include/webservice_epoll.h"

using namespace std;

void ChangePath(char *file) {
    char pwd_path[INFO_BUFF] = "";
    //获取当前绝对路径
    char *path = getenv("PWD");
    strcpy(pwd_path, path);
    strcat(pwd_path, file);
    //修改目录
    chdir(pwd_path);
}

void SetFL(int cfd) {
    int flag = fcntl(cfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd, F_SETFL, flag);
}

void AddEpoll(int epfd, epoll_event *ev) {
    sockaddr_in caddr;
    memset(&caddr, 0, sizeof(caddr));
    socklen_t clen = sizeof(caddr);
    int cfd = Accept(ev->data.fd, (sockaddr *) &caddr, &clen);
    SetFL(cfd);
    //设置epoll_event 并上树
    ev->events = EPOLLIN;
    ev->data.fd = cfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, ev);
    cout << "welcome client, ip is" << inet_ntoa(caddr.sin_addr) << " port is " << ntohs(caddr.sin_port)
         << endl;
}

void DelEpoll(int epfd, epoll_event *ev) {
    epoll_ctl(epfd, EPOLL_CTL_DEL, ev->data.fd, ev);
    close(ev->data.fd);
}

void SendResponse(char *strfile, int epfd, epoll_event *ev) {
    struct stat s;
    //没有该文件或文件夹
    if (stat(strfile, &s) < 0) {
        cout << "not find file" << endl;
        SendHeader(ev->data.fd, 404, "NOT FOUND", get_mime_type("*.html"), 0);
        SendFile(ev->data.fd, "error.html", epfd, ev, 1);
    } else {
        //文件名是文件
        if (S_ISREG(s.st_mode)) {
            cout << "file" << endl;
            SendHeader(ev->data.fd, 200, "OK", get_mime_type(strfile), s.st_size);
            SendFile(ev->data.fd, strfile, epfd, ev, 1);
        } else if (S_ISDIR(s.st_mode)) {
            //文件名是文件夹
            cout << "dir" << endl;
            SendHeader(ev->data.fd, 200, "OK", get_mime_type("*.html"), 0);
            SendFile(ev->data.fd, _DIR_PREFIX_FILE_, epfd, ev, 0);
            SendDir(ev, strfile);
            SendFile(ev->data.fd, _DIR_TAIL_FILE_, epfd, ev, 1);
        }
    }
}

void SendDir(epoll_event *ev, char *strfile) {
    struct dirent **mylist = nullptr;
    int n = scandir(strfile, &mylist, nullptr, alphasort);
    int len = -1;
    char buff[BUFF_SIZE] = "";
    for (int i = 0; i < n; i++) {
        //组合成html格式并发送
        if (mylist[i]->d_type == DT_DIR) {
            len = sprintf(buff, "<li><a href = %s/>%s</a></li>", mylist[i]->d_name, mylist[i]->d_name);
        } else {
            len = sprintf(buff, "<li><a href = %s>%s</a></li>", mylist[i]->d_name, mylist[i]->d_name);
        }
        send(ev->data.fd, buff, len, 0);
        delete mylist[i];
    }
    delete mylist;
}

void Main() {
    ChangePath(PATH);
    //忽略signal，防止服务器挂掉
    signal(SIGPIPE, SIG_IGN);
    //epoll
    int lfd = tcp4bind(PORT, nullptr);
    listen(lfd, 128);
    int epfd = epoll_create(1);
    epoll_event ev, evs[BUFF_SIZE];
    ev.data.fd = lfd;
    ev.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    while (true) {
        int nread = epoll_wait(epfd, evs, BUFF_SIZE, -1);
        if (nread < 0) {
            perror("");
            break;
        } else {
            for (int i = 0; i < nread; i++) {
                // 服务端，上树
                if (evs[i].data.fd == lfd && evs[i].events & EPOLLIN) {
                    AddEpoll(epfd, &evs[i]);
                } else if (evs[i].events & EPOLLIN) {   //客户端
                    ReadClientRequest(epfd, &evs[i]);
                }
            }
        }
    }
}

void ReadClientRequest(int epfd, struct epoll_event *ev) {
    char buff[BUFF_SIZE] = "";
    char temp[BUFF_SIZE] = "";

    int n = Readline(ev->data.fd, buff, sizeof(buff));
    if (n <= 0) {
        cout << "close clf error" << endl;
        DelEpoll(epfd, ev);
        return;
    }
    cout << buff;
    int ret = 0;
    //读取请求行
    while ((ret = Readline(ev->data.fd, temp, sizeof(temp))) > 0);
    cout << "read over" << endl;
    //获取数据  请求  数据  协议
    char method[INFO_BUFF] = "";
    char content[INFO_BUFF] = "";
    char protocol[INFO_BUFF] = "";
    sscanf(buff, "%[^ ] %[^ ] %[^ \r\n]", method, content, protocol);
    cout << method << " " << content << " " << protocol << endl;
    //只处理get请求
    if (strcasecmp(method, "get") == 0) {
        char *strfile = content + 1;
        //文件名修改编码
        strdecode(strfile, strfile);
        if (*strfile == 0) {
            strfile = "./";
        }
        SendResponse(strfile, epfd, ev);
    }
}

void SendHeader(int cfd, int code, char *info, char *filetype, int length) {
    char buff[BUFF_SIZE] = "";
    //发送状态行和消息部头
    int len = sprintf(buff, "HTTP/1.1 %d %s\r\n", code, info);
    send(cfd, buff, len, 0);
    len = sprintf(buff, "Content-Type:%s\r\n", filetype);
    send(cfd, buff, len, 0);
    if (length > 0) {
        len = sprintf(buff, "Content-Length:%d\r\n", length);
        send(cfd, buff, len, 0);
    }
    send(cfd, "\r\n", 2, 0);
}

void SendFile(int cfd, char *path, int epfd, epoll_event *ev, int flag) {
    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        cout << "文件不存在" << endl;
        return;
    }
    char buff[BUFF_SIZE];
    //发送文件
    while (true) {
        int len = read(fd, buff, sizeof(buff));
        if (len < 0) {
            break;
        } else if (len == 0) {
            break;
        } else {
            int n = 0;
            n = send(cfd, buff, len, 0);
            cout << n << endl;
        }
    }
    close(fd);
    //判断是否需要关闭节点
    if (flag) {
        DelEpoll(epfd, ev);
    }
}
